package com.osilon.asm;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;

import com.osilon.updater.Updater;

public class OsilonClassNode extends ClassNode {
	public List<OsilonMethodNode> public_methods = new ArrayList<OsilonMethodNode>();
	public List<OsilonMethodNode> static_methods = new ArrayList<OsilonMethodNode>();
	
	public List<FieldNode> public_fields = new ArrayList<FieldNode>();
	public List<FieldNode> static_fields = new ArrayList<FieldNode>();
	
	public Map<Object, List<OsilonMethodNode>> constants = new HashMap<Object, List<OsilonMethodNode>>();
	
	public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
		FieldNode node = new FieldNode(access, name, desc, signature, value);
		
		if(Modifier.isStatic(access)) {
			static_fields.add(node);
		} else {
			public_fields.add(node);
		}
		
		fields.add(node);
		
		return node;
	}
	
	public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
		OsilonMethodNode node = new OsilonMethodNode(this, access, name, desc, signature, exceptions);
		
		if(Modifier.isStatic(access)) {
			static_methods.add(node);
		} else {
			public_methods.add(node);
		}
		
		methods.add(node);
		
		return node;
	}
	
	public void addSetter(String setterName, String setterDesc, String fieldName, String fieldDesc) {
		MethodNode node = new MethodNode(Opcodes.ACC_PUBLIC, setterName, "(" + setterDesc + ")V", null, null);
		node.visitVarInsn(Opcodes.ALOAD, 0);
		node.visitVarInsn(Type.getType(setterDesc).getOpcode(Opcodes.ILOAD), 1);
		node.visitFieldInsn(Opcodes.PUTFIELD, super.name, fieldName, fieldDesc);
		node.visitInsn(Opcodes.RETURN);
			
		methods.add(node);
	}
	
	public void addGetter(String getterName, String getterDesc, String fieldName, String fieldDesc) {
		MethodNode node = new MethodNode(Opcodes.ACC_PUBLIC, getterName, "()" + getterDesc, null, null);
		node.visitVarInsn(Opcodes.ALOAD, 0);
		node.visitFieldInsn(Opcodes.GETFIELD, super.name, fieldName, fieldDesc);
		node.visitInsn(Type.getType(getterDesc).getOpcode(Opcodes.IRETURN));
		
		methods.add(node);
	}
	
	public static void addStaticGetter(String getterName, String getterDesc, String fieldOwner, String fieldName, String fieldDesc) {
		OsilonClassNode ocn = Updater.getClassCache().getClass("net/minecraft/server/MinecraftServer");
		MethodNode node = new MethodNode(Opcodes.ACC_PUBLIC, getterName, "()" + getterDesc, null, null);
		node.visitFieldInsn(Opcodes.GETSTATIC, fieldOwner, fieldName, fieldDesc);
		node.visitInsn(Type.getType(getterDesc).getOpcode(Opcodes.IRETURN));
		
		ocn.methods.add(node);
	}
	
	public void addMethodDelegate(String methodName, String methodDesc, String origMethodName, String origMethodDesc, boolean p) {
		MethodNode node = new MethodNode(Opcodes.ACC_PUBLIC, methodName, methodDesc, null, null);
		Type[] types = Type.getArgumentTypes(methodDesc);
		
		node.visitVarInsn(Opcodes.ALOAD, 0);
		
		for(int x = 0; x < types.length; x++) {
			node.visitVarInsn(types[x].getOpcode(Opcodes.ILOAD), x + 1);
		}
		
		node.visitMethodInsn(p ? Opcodes.INVOKESPECIAL : Opcodes.INVOKEVIRTUAL, super.name, origMethodName, origMethodDesc);
		node.visitInsn(Type.getReturnType(methodDesc).getOpcode(Opcodes.IRETURN));
		
		methods.add(node);
	}
	
	public void addField(String fieldName, String fieldDesc, Object value) {
		FieldNode node = new FieldNode(Opcodes.ACC_PUBLIC, fieldName, fieldDesc, null, value);
		
		fields.add(node);
	}
}
